package com.bvgol.examples.dubbo.zk.util;/**
 * @Classname DubboGenericUtil
 * @Description TODO
 * @Date 2022/4/20 14:31
 * @Created by GUOCHEN
 */

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;

/**
 * @program: common-components-group
 * @description:
 * @author: GUOCHEN
 * @create: 2022/04/20 14:31
 */
@Component
@Slf4j
public class DubboGenericUtil {

    @Value("${dubbo.application.name}")
    private String applicationName;

    @Value("${prop.registryConfigAddress}")
    private String registryConfigAddress;

    @Value("${prop.dubbo.timeout}")
    private Integer timeout;

    /**
     *
     * @param interfaceReference 调用接口的全路径
     * @param args 参数数组
     * @return
     * @throws Exception
     */
    public ServiceResult<String> generic(String interfaceReference, Object[] args) throws Exception{
        if (ArrayUtils.isEmpty(args)){
            ServiceResult<String> serviceResult = new ServiceResult<>();
            serviceResult.setResult("[genericByParamsLength 调用时,args 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        String methodName="notifyByBjy";
        String[] paramClassType = new String[args.length];
        for (int i = 0; i < args.length; i++) {
            paramClassType[i] = args[i].getClass().getName();
        }
        return this.generic(interfaceReference, methodName, paramClassType, args,null);
    }

    /**
     *
     * @param interfaceReference 调用接口的全路径
     * @param args 参数数组
     * @param version 版本号.没有就不写,传null
     * @return
     * @throws Exception
     */
    public ServiceResult<String> generic(String interfaceReference, Object[] args,String version) throws Exception{
        if (ArrayUtils.isEmpty(args)){
            ServiceResult<String> serviceResult = new ServiceResult<>();
            serviceResult.setResult("[genericByParamsLength 调用时,args 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        String methodName="notifyByBjy";
        String[] paramClassType = new String[args.length];
        for (int i = 0; i < args.length; i++) {
            paramClassType[i] = args[i].getClass().getName();
        }
        return this.generic(interfaceReference, methodName, paramClassType, args,version);
    }

    /**
     *
     * @param interfaceReference 调用接口的全路径
     * @param methodName  被调用方法名字
     * @param args 参数数组
     * @return
     * @throws Exception
     */
    public ServiceResult<String> generic(String interfaceReference, String methodName, Object[] args) throws Exception{
        if (ArrayUtils.isEmpty(args)){
            ServiceResult<String> serviceResult = new ServiceResult<>();
            serviceResult.setResult("[genericByParamsLength 调用时,args 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        String[] type = new String[]{"java.lang.String","java.lang.Long","java.lang.Integer"};
        String[] paramClassType = new String[args.length];
        for (int i = 0; i < args.length; i++) {
            paramClassType[i] = args[i].getClass().getName();
        }
        return this.generic(interfaceReference, methodName, paramClassType, args);
    }

    /**
     *
     * @param interfaceReference 调用接口的全路径
     * @param methodName 被调用方法名字
     * @param paramClassType 参数类型数组
     * @param args 参数数组
     * @return
     */
    public ServiceResult<String> generic(String interfaceReference, String methodName, String[] paramClassType, Object[] args) {
        //参数验证
        ServiceResult<String> serviceResult = new ServiceResult<>();
        if (StringUtils.isEmpty(interfaceReference) || StringUtils.isEmpty(interfaceReference) || ArrayUtils.isEmpty(paramClassType)) {
            serviceResult.setResult("[generic 调用时,interfaceReference||interfaceReference||classType 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        if (ArrayUtils.isEmpty(paramClassType)||ArrayUtils.isEmpty(args)){
            serviceResult.setResult("[generic 调用时,classType||args 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        if (paramClassType.length!=args.length){
            serviceResult.setResult("[generic 调用时,classType与args 长度不一致 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        try {
            ReferenceConfig<GenericService> reference = initGenetic(interfaceReference,null);
            Object result = genericInvoke(reference,methodName, paramClassType, args);
            String printLog = printLog(interfaceReference, methodName, paramClassType, args, result);
            serviceResult.setResult(printLog);
            System.out.println(JSON.toJSONString(serviceResult));
            return serviceResult;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("[generic] Exception",e);
            serviceResult.setResult("[generic.Exception]==>" + e);
            serviceResult.setSuccess(false);
            return serviceResult;
        }
    }

    /**
     * 全参方法
     * @param interfaceReference 调用接口的全路径
     * @param methodName 被调用方法名字
     * @param paramClassType 参数类型数组
     * @param args 参数数组
     * @param version 版本号.没有就不写,传null
     * @return
     */
    public ServiceResult<String> generic(String interfaceReference, String methodName, String[] paramClassType, Object[] args,String version) {
        //参数验证
        ServiceResult<String> serviceResult = new ServiceResult<>();
        if (StringUtils.isEmpty(interfaceReference) || StringUtils.isEmpty(interfaceReference) || ArrayUtils.isEmpty(paramClassType)) {
            serviceResult.setResult("[generic 调用时,interfaceReference||interfaceReference||classType 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        if (ArrayUtils.isEmpty(paramClassType)||ArrayUtils.isEmpty(args)){
            serviceResult.setResult("[generic 调用时,classType||args 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        if (paramClassType.length!=args.length){
            serviceResult.setResult("[generic 调用时,classType与args 长度不一致 必须有有效值]");
            serviceResult.setSuccess(false);
            return serviceResult;
        }
        try {
            ReferenceConfig<GenericService> reference = initGenetic(interfaceReference,version);
            Object result = genericInvoke(reference,methodName, paramClassType, args);
            String printLog = printLog(interfaceReference, methodName, paramClassType, args, result);
            serviceResult.setResult(printLog);
//            System.out.println(JSON.toJSONString(serviceResult));
            return serviceResult;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("[generic] Exception",e);
            serviceResult.setResult("[generic.Exception]==>" + e);
            serviceResult.setSuccess(false);
            return serviceResult;
        }
    }

    /**
     * 初始化泛化调用对象
     * @param interfaceReference 调用接口的全路径
     * @param version 版本号.没有就不写,传null
     * @return
     * @throws Exception
     */
    private ReferenceConfig<GenericService> initGenetic(String interfaceReference,String version) throws Exception {
        ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
        // 注册中心配置
        // 当前dubbo consumer的application配置，不设置会直接抛异常
        try {
            reference.setApplication(new ApplicationConfig(applicationName));
            // 注册中心这里需要配置上注册中心协议，例如下面的zookeeper
            reference.setRegistry(new RegistryConfig(registryConfigAddress));
            // 设置调用的reference属性，下面只设置了协议、接口名、版本、超时时间
            reference.setInterface(interfaceReference);
            if (StringUtils.isNotEmpty(version)){
                reference.setVersion(version);
            }
            reference.setTimeout(timeout);
            // 声明为泛化接口
            reference.setGeneric(true);
            return reference;
        } catch (Exception e) {
            System.out.println(e);
            log.error("[initGenetic.Exception]==>{}", e);
            throw new Exception(e);
        }
    }

    /**
     * 执行dubbo泛化调用
     * @param reference 接口配置对象
     * @param paramClassType 参数类型数组
     * @param args      参数数组
     * @param methodName 被调用方法名字
     * @return
     * @throws Exception
     */
    private Object genericInvoke(ReferenceConfig<GenericService> reference,String methodName, String[] paramClassType, Object[] args) throws Exception {
        Object result = null;
        try {
            GenericService genericService = reference.get();
            result = genericService.$invoke(methodName, paramClassType, args);
        } catch (Exception e) {
            log.error("[genericInvoke.Exception]==>{}", e);
            throw new Exception(e);
        } finally {
            if (reference != null) {
                reference.destroy();
            }
        }
        return result;
    }

    /**
     *  打印日志
     * @param interfaceReference 调用接口的全路径
     * @param methodName 调用方法的名字
     * @param classType
     * @param args
     * @param result
     * @return
     */
    private String printLog(String interfaceReference, String methodName, String[] classType, Object[] args, Object result) {
        JSONObject logPrint = new JSONObject(new LinkedHashMap<>());
        logPrint.put("interfaceReference", interfaceReference);
        logPrint.put("classType", classType);
        logPrint.put("methodName", methodName);
        logPrint.put("args", args);
        logPrint.put("result", result);
        log.info("[generic]==>{}", logPrint);
        return JSON.toJSONString(logPrint);
    }

}
